package appli.product;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Class managing the best products to buy for a buyer agent.
 */
public class ProductManager implements Serializable{

	public enum State {VisitingSores, BuyingProducts};

	/**
	 * <productName, productsToBuy>
	 * If (productsToBuy != null) then (orders == null)
	 */
	private Map<String, ProductManagement> productsToBuy;

	/**
	 * <storeName, productsToBuyAtTheStore>
	 * If (orders != null) then (productsToBuy == null)
	 */
	private Map<String, List<ProductOrder>> orders;

	/**
	 * VisitingStores : the buyer agent is visiting stores to check for the best prices
	 * BuyingProducts : the buyer agent is visiting stores to buy the products at the best prices
	 */
	public State currentState;

	public ProductManager(List<ProductOrder> productsToBuy){
		visitingStoresMode(productsToBuy);
	}

	public void add(String storeName, Product p){
		if(currentState.equals(State.VisitingSores)){
			ProductManagement pm = productsToBuy.get(p.getName());
			if(pm != null){
				List<ProductStore> lps = pm.products;
				if(pm.actualQuantity < pm.totalQuantity || p.getPrice() < lps.get(lps.size()-1).price)
					pm.add(new ProductStore(p.getQuantity(), p.getPrice(), storeName));
			}
		}
	}

	public void visitingStoresMode(List<ProductOrder> productsToBuy){
		currentState = State.VisitingSores;
		orders = null;
		this.productsToBuy = new HashMap<String, ProductManagement>();
		for(ProductOrder po : productsToBuy){
			ProductManagement pm = new ProductManagement(po.productName, po.quantity);
			this.productsToBuy.put(po.productName, pm);
		}
	}

	public List<String> buyingProductsMode(){
		currentState = State.BuyingProducts;
		orders = new HashMap<String, List<ProductOrder>>();
		for(ProductManagement pm : productsToBuy.values()){
			String productName = pm.productName;
			List<ProductStore> lps = pm.products;
			for(ProductStore ps : lps){
				List<ProductOrder> lpo = orders.get(ps.store);
				if(lpo == null){
					lpo = new ArrayList<ProductOrder>();
					orders.put(ps.store, lpo);
				}
				lpo.add(new ProductOrder(productName, ps.quantity));
			}
		}
		productsToBuy = null;
		return new ArrayList<String>(orders.keySet());
	}

	public List<ProductOrder> getProductsToBuy(String storeName){
		if(currentState.equals(State.BuyingProducts)){
			return orders.get(storeName);
		}
		else return null;
	}

	/* *******************************
	 *         PRIVATE CLASS         *
	 *********************************/

	private class ProductStore implements Serializable{
		public int quantity;
		public int price;
		public String store;

		public ProductStore(int quantity, int price, String store) {
			super();
			this.quantity = quantity;
			this.price = price;
			this.store = store;
		}
	}

	private class ProductManagement implements Serializable{
		public int totalQuantity;
		public int actualQuantity;
		public String productName;
		public List<ProductStore> products;

		public ProductManagement(String productName, int totalQuantity) {
			this.totalQuantity = totalQuantity;
			this.actualQuantity = 0;
			this.productName = productName;
			this.products = new ArrayList<ProductStore>();
		}

		public void add(ProductStore ps){
			if(products.size() == 0)
				internalAdd(ps, 0);
			else {
				for(int i = 0; i < products.size(); i++){
					if(ps.price < products.get(i).price){
						internalAdd(ps, i);
						break;
					}
					else if(i == products.size()-1){
						internalAdd(ps, i+1);
						break;
					}
				}
			}
		}

		public void internalAdd(ProductStore ps, int pos){
			if(pos == products.size())
				products.add(ps);
			else products.add(pos, ps);
			actualQuantity += ps.quantity;
			if(actualQuantity > totalQuantity){
				int k = products.size() - 1;
				int diff = actualQuantity - totalQuantity;
				while(diff > 0){
					ProductStore ps2 = products.get(k);
					if(ps2.quantity <= diff){
						diff -= ps2.quantity;
						products.remove(k);
						k--;
					}
					else{
						ps2.quantity -= diff;
						break;
					}
				}
				actualQuantity = totalQuantity;
			}
		}
	}
	
	@Override
	public String toString(){
		String s = "";
		for(ProductManagement pm : productsToBuy.values()){
			s += "nomProduit : " + pm.productName + " totalQuantity = " + pm.totalQuantity + " actualquantity = " + pm.actualQuantity + "\n";
			for(ProductStore ps : pm.products){
				s += ("[" + ps.store + ":nb=" + ps.quantity + " price=" + ps.price + "] ");
			}
			s += "\n";
		}
		return s;
	}
}
